#ifndef slak_geom_ArcBall_h
#define slak_geom_ArcBall_h

#include <math.h>
#include <slak/geom/Quat.h>

namespace slak {
	namespace geom {
		struct ArcBall {
			Quat start, now;
			float x, y;
			float sw, sh; // screen dimensions

			ArcBall(int _sw = 1024, int _sh = 768) 
				: start(), now(), sw(_sw), sh(_sh) {}

			void reset() {
				start = Quat();
				now = Quat();
			}

			void click(float _x, float _y) 
			{
				start = now * start;
				now = Quat();
				x = _x;
				y = _y;
			}

			void clickScaled(int x, int y) 
			{
				click( (((float)x / ((sw  - 1.0) 
						      / 2.0)) - 1.0),
				      (((float)y / ((sh - 1.0) 
						      / 2.0)) - 1.0));
			}

			void drag(float x, float y)
			{
				float dx,dy,dz;
				float mx,my,mz;
				onUnitSphere(this->x, this->y, &dx, &dy, &dz);
				onUnitSphere(x, y, &mx, &my, &mz);

				now.x = dy*mz - dz*my;
				now.y = dz*mx - dx*mz;
				now.z = dx*my - dy*mx;
				now.w = dx*mx + dy*my + dz*mz;

				now = now.normalize();
			}

			void dragScaled(int x, int y) 
			{
				drag( (((float)x / ((sw  - 1.0) 
						      / 2.0)) - 1.0),
				      (((float)y / ((sh - 1.0) 
						      / 2.0)) - 1.0));
			}

			void getMatrix(Matrix m) const
			{
				(now * start).conjugate().toMatrix(m);
			}

			static void onUnitSphere(float mx, float my,
						 float* x, float* y, float* z)
			{
				*x = mx; // should divide radius
				*y = my;
				float mag = (*x)*(*x) + (*y)*(*y);
				if (mag > 1.0) {
					float scale = 1.0f / sqrtf(mag);
					*x *= scale;
					*y *= scale;
					*z = 0;
				} else {
					*z = sqrt(1.0 - mag);
				}
			}

		};
	}
}

#endif
